#include "hashtable.h"
#include <string.h>

void ht_init(HashTable *ht, size_t size, HashFunc hf, detor dtor){
	size_t i;
	ht->size = size;
	ht->dtor = dtor;
	if (hf == 0) 
	{
		ht->hashfunc = jenkins_one_at_a_time_hash;
	}
	else 
	{
		ht->hashfunc = hf;
	}
	ht->table = (List **)malloc(sizeof(List *)* ht->size);
	for (i = 0; i <size; i++)
	{
		ht->table[i] = NULL;
	}
}

void ht_destroy(HashTable *ht){
	int i;
	List *p;
	for (i = 0; i < (int)ht->size; i++)
	{
		p = ht->table[i];
		while (p != NULL)
		{
			free(p->data);
			free(p->key);

			p = ht->table[i]->next;
			if (ht->dtor)
			{
				ht->dtor(p->data);
			}
			free(ht->table[i]);
			ht->table[i] = p;
		}
	}
	free(ht->table);
	ht->size = 0;
	ht->dtor = NULL;
	ht->hashfunc = NULL;
}

void ht_set(HashTable *ht, char *key, Pointer data, size_t size){
	unsigned j;
	List *p;
	j = ht->hashfunc(key) % ht->size;
	p = ht->table[j];
	while (p != NULL) 
	{
		if (strcmp(p->key, key) == 0)
		{
			p->data = data;
			break;
		}
		else
			p = p->next;
	}
	if (p == NULL) 
	{
		List *p1;
		p1 = (List*)malloc(sizeof(List));
 		p1->data = (Pointer)malloc(size);
		p1->key = (char *)malloc(sizeof(char)*(strlen(key)+1));
		//memcpy(p1->data, data, size); 
		//memcpy(p1->key, key, strlen(key));
		strcpy (p1->data, data);
		strcpy (p1->key, key);
		p1->next = NULL;
		if (ht->table[j] == NULL)
			ht->table[j] = p1;
		else 
		{
			p = ht->table[j];
			while (p->next != NULL)
				p = p->next;
			p->next = p1;
		}
		//free(p1->key);
		//free(p1->data);
		//free(p1);
	}
}

Pointer ht_get(HashTable *ht, char *key){
	unsigned j;
	List *p;
	j = ht->hashfunc(key) % ht->size;
	p = ht->table[j];
	while (p != NULL) 
	{
		if (strcmp(p->key, key) == 0)
			return p->data;
		else
			p = p->next;
	}
	return NULL;
}

int ht_has(HashTable *ht, char *key){
	unsigned j;
	List *p;
	j = ht->hashfunc(key) % ht->size;
	p = ht->table[j];
	while (p != NULL) 
	{
		if (strcmp(p->key, key) == 0)
			return 1;
		else
			p = p->next;
	}
	return 0;
}

void ht_delete(HashTable *ht, char *key){
	unsigned j;
	j = ht->hashfunc(key) % ht->size;
	if (ht_has(ht, key) == 1)
	{
		List *p, *p2;
		p = ht->table[j];
		if (!strcmp(p->key, key))
		{
			ht->table[j] = ht->table[j]->next;
			p->next = NULL;
			p->key = NULL;
			if (ht->dtor)
			{
				ht->dtor(p->data);
			}
			free(p);
		}
		else 
		{
			while (strcmp(p->next->key, key) != 0)
				p = p->next;
			p2 = p->next;
			p->next = p2->next;
			p2->next = NULL;
			p2->key = NULL;
			if (ht->dtor)
			{
				ht->dtor(p2->data);
			}
			free(p2);
		}
	}
}

void ht_traverse(HashTable *ht, void(*f)(char *key, Pointer data)){
	size_t i;
	List *p;
	for (i = 0; i < ht->size; i++)
	{
		p = ht->table[i];
		while (p != NULL) 
		{
			f(p->key, p->data);
			p = p->next;
		}
	}
}



unsigned jenkins_one_at_a_time_hash(char *key) {
	unsigned hash = 0;

	for (; *key; ++key)
	{
		hash += *key;
		hash += (hash << 10);
		hash ^= (hash >> 6);
	}
	hash += (hash << 3);
	hash ^= (hash >> 11);
	hash += (hash << 15);
	return hash;
}